package com.danicsoft.daide.command;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.danicsoft.daide.token.StandardToken;
import com.danicsoft.daide.token.Token;
import com.danicsoft.daide.token.TokenMap;
import com.danicsoft.daide.token.TokenGroup;



/**
 * Implements the HLO command. From Eric Wald's <a
 * href="http://pydip.brainshell.org/docs/syntax.html">protocol specification</a>,
 * v0.14:
 * <h4><a name="MDF">client_message = MDF</a></h4>
 * <p>
 * The server will reply with the map definition of the form:
 * </p>
 * <h4>server_message = MDF (powers) (provinces) (adjacencies)</h4>
 * <!--
 * <h4>powers = power power ...</h4>
 * -->
 * <p>
 * <b>(powers)</b> is the list of powers. For example, <b>(AUS ENG FRA GER ITA
 * RUS TUR)</b>.
 * </p>
 * <!--
 * <h4>provinces = (supply_centres) (non_supply_centres)</h4>
 * -->
 * <p>
 * <b>(provinces)</b> is the list of provinces. This is broken further into two
 * sections &mdash; <b>((supply_centres) (non_supply_centres))</b>.
 * </p>
 * <!--
 * <h4>supply_centres = (mdf_power centre centre ...) (mdf_power centre centre
 * ...) ...</h4>
 * --> <!--
 * <h4>centre = Inland_SC</h4>
 * --> <!--
 * <h4>centre = Sea_SC</h4>
 * --> <!--
 * <h4>centre = Coastal_SC</h4>
 * --> <!--
 * <h4>centre = Bicoastal_SC</h4>
 * --> <!--
 * <h4>mdf_power = power</h4>
 * --> <!--
 * <h4>mdf_power = UNO</h4>
 * --> <!--
 * <h4>mdf_power = (power power ...)</h4>
 * -->
 * <p>
 * <b>(supply_centres)</b> is of the form <b>((power centre centre ...) (power
 * centre centre ...) ...)</b> where <b>centre</b> is a supply centre
 * province, and <b>power</b> is the power for which the centre is a home
 * centre. <b>power</b> is <b>UNO</b> for neutral supply centres. Also, in the
 * case of a variant where a supply centre is a home centre for two or more
 * powers, <b>power</b> may be of the form <b>(power power ...)</b>. Note that
 * the home centres are the locations where a power may build new units, but are
 * not necessarily the location where the units start the game.
 * </p>
 * <!--
 * <h4>non_supply_centres = province province ...</h4>
 * -->
 * <p>
 * <b>(non_supply_centres)</b> is the list of all non-supply-centre provinces.
 * </p>
 * <!--
 * <h4>adjacencies = (prov_adj) (prov_adj) ...</h4>
 * -->
 * <p>
 * <b>(adjacencies)</b> is the list of what is next to what. It is of the form
 * <b>((prov_adj) (prov_adj) ...)</b>.
 * </p>
 * <!--
 * <h4>prov_adj = province (coast_adj) (coast_adj) ...</h4>
 * --> <!--
 * <h4>prov_adj != (province property property ...) (coast_adj) (coast_adj) ...</h4>
 * --> <!--
 * <h4>coast_adj = mdf_unit adj_prov adj_prov ...</h4>
 * -->
 * <p>
 * <b>(prov_adj)</b> is of the form <b>(province (unit_type adj_prov adj_prov
 * ...) (unit_type adj_prov adj_prov ...) ...)</b><!-- or <b>((province
 * property property ...) (unit_type adj_prov adj_prov ...) (unit_type adj_prov
 * adj_prov ...) ...)</b>-->.
 * </p>
 * <!--
 * <p>
 * <b>property</b> is one of:
 * </p>
 * <ul>
 * <li><b>property != ICE</b> &mdash; the province is an ice sector: it is
 * only open during spring and summer</li>
 * <li><b>property != HAR</b> &mdash; the province is a port or harbor: both
 * armies and fleets can be there, but fleets there can convoy</li>
 * </ul>
 * -->
 * <p>
 * <b>unit_type</b> is one of:
 * </p>
 * <ul>
 * <li><b>mdf_unit = AMY</b> &mdash; list of provinces an army can move to</li>
 * <li><b>mdf_unit = FLT</b> &mdash; list of provinces a fleet can move to</li>
 * <li><b>mdf_unit = (FLT coast)</b> &mdash; list of provinces a fleet can
 * move to from the given coast.</li>
 * </ul>
 * <p>
 * <b>adj_prov</b> is one of:
 * </p>
 * <ul>
 * <li><b>adj_prov = province</b> &mdash; a province that can be moved to</li>
 * <li><b>adj_prov = (province coast)</b> &mdash; a coast of a province that
 * can be moved to</li>
 * <!--
 * <li><b>adj_prov != (province border_type)</b> &mdash; a province that can
 * be moved to only through a special type of border</li>
 * -->
 * </ul>
 * <!--
 * <p>
 * <b>border_type</b> is one of:
 * </p>
 * <ul>
 * <li><b>border_type != DIF</b> &mdash; the border is difficult-passable:
 * moves through it have an attack strength of zero, and support cannot be given
 * through it</li>
 * <li><b>border_type != (CNL province)</b> &mdash; the border is a canal: the
 * owner of the unit in <b>province</b> must give permission to move through it</li>
 * </ul>
 * -->
 * <p>
 * The type of province can be determined from its adjacencies. An inland
 * province only has army adjacencies. A sea province only has fleet
 * adjacencies. A coastal province has both. A multi-coastal province has
 * entries for each coast.
 * </p>
 * <!--
 * <h4>coast_adj = AMY</h4>
 * -->
 * <p>
 * Note that for a land province surrounded by sea, there will be an <b>AMY</b>
 * entrance with no provinces listed.
 * </p>
 * <p>
 * So, the map for Standard would (with some provinces skipped, and carriage
 * returns added for clarity):
 * </p>
 * 
 * <pre>
 *      &lt;b&gt;
 *       MDF (AUS ENG FRA GER ITA RUS TUR)
 *       (((AUS TRI BUD VIE) (ENG LON LVP EDI) ...
 *       (UNO POR SPA BEL ...)) (ADR AEG ALB APU ...))
 *       ((ANK (FLT ARM BLA CON) (AMY CON SMY ARM))
 *       (ADR (FLT ION ALB TRI VEN APU))
 *       (CON (AMY BUL SMY ANK) (FLT BLA (BUL ECS) (BUL SCS) AEG SMY ANK))
 *       (BUL (AMY GRE SER RUM CON) ((FLT ECS) RUM BLA CON) ((FLT SCS) CON AEG GRE))
 *       (BOH (AMY TYR MUN SIL GAL VIE))
 *       ...)
 *       &lt;/b&gt;
 * </pre>
 * 
 * <p>
 * No map will ever contain a province abbreviation which is also has a defined
 * meaning in the Diplomacy AI Development Environment syntax, except that the
 * tokens for provinces and powers on the Standard map may be reused on variant
 * maps.
 * </p>
 * <p>
 * No map will ever contain more than 256 provinces.
 * </p>
 * <p>
 * Once the client has received the Map Definition, it should then reply with
 * <b>YES (MAP ('name'))</b> if it is able to use the map, or <b>REJ (MAP
 * ('name'))</b> if it is not. If the client wishes to do some processing on
 * the map before the game starts, then it should do this before sending <b>YES
 * (MAP ('name'))</b>.
 * </p>
 * 
 * @author Daniel Yule (daniel.yule@gmail.com)
 * @version 1.0
 */
public class MapDefinitionCommand extends QueryableCommand {

	/**
	 * A <code>List</code> of powers that this map defines, listed as
	 * <code>Token</code>s.
	 */
	private List<StandardToken> powerList;

	/**
	 * A <code>Map</code> from power <code>Token</code>s to a list of
	 * province <code>Token</code>s. The power <code>Token</code>s are the
	 * owner of the supply centres located in the province specified by the
	 * province <code>Token</code>s.
	 */
	private Map<Token, List<Token>> centreOwnership;

	// TODO Make this work with standard tokens instead.

	/**
	 * A <code>List</code> of provinces that do not have supply centres
	 * associated with them.
	 */
	private List<StandardToken> extraProvinces;

	/**
	 * A <code>Map</code> of all the provincial adjacencies, keyed on
	 * province. For more information consult {@link prov_adj}.
	 */
	private Map<Token, prov_adj> provAdj;
	
	/**
	 * A <code>List</code> of provinces on this map.
	 */
	private List<Token> provinceList;

	/**
	 * Construct a </code>MapDefinitionCommand</code> from the <code>TokenGroup</code>
	 * specified.
	 * 
	 * @param tokens
	 *        A <code>TokenGroup</code> that is either empty or contains three
	 *        sub groups: A list of powers, supply centre and provincial
	 *        information and a list of adjacencies.
	 * @throws ParseException
	 *         If there was a problem parsing the <code>TokenGroup</code>
	 */
	public MapDefinitionCommand(TokenGroup tokens) throws ParseException {
		super(tokens);

	}

	/**
	 * Create a <code>MapDefinitionCommand</code> that can be used to query a
	 * server for the map definition for this game.
	 */
	public MapDefinitionCommand() {
		super();
	}

	@Override
	public String toString() {
		return "Map definition";

	}

	@Override
	public void doNonQueryIO(TokenIO tokenIO, TokenGroup tokens) throws ParseException {

		// load the powers
		TokenGroup powers = tokenIO.subGroup(tokens);

		powerList = tokenIO.doTokenList(powers, powerList, TokenMap.powers);

		// load the province and supply centre info
		TokenGroup provinces = tokenIO.subGroup(tokens);

		// load the first subgroup of provinces (supply centre ownership) as a
		// hash map from power to a list of provinces
		centreOwnership = tokenIO.doHashOnFirstToken(tokenIO.subGroup(provinces), centreOwnership, TokenMap.powers, TokenMap.provinces);

		// load the non-supply centre provinces as a list
		extraProvinces = tokenIO.doTokenList(tokenIO.subGroup(provinces), extraProvinces, TokenMap.provinces);

		// Load the province adjacencies
		TokenGroup adjacencies = tokenIO.subGroup(tokens);

		provAdj = tokenIO.doHashOnFirst(adjacencies, provAdj, new prov_adj(), TokenMap.provinces);	
			
		
	}

	@Override
	public byte getType() {
		return TokenMap.Commands.MDF;
	}

	/**
	 * Contains information for the adjacencies of a single province. A province
	 * can be adjacent to another province in three ways: on the coast, over the
	 * water, or over land. This class contains information for all the types of
	 * adjacencies for a particular province.
	 * 
	 * @author Daniel Yule (daniel.yule@gmail.com)
	 * @version 1.0
	 */
	public class prov_adj implements IOTarget {

		/**
		 * A <code>Map</code> from unit types to provinces or coasts.
		 */
		public Map<Token, List<Token>> adjacencies = new HashMap<Token, List<Token>>();

		public void doIO(TokenIO tokenIO, TokenGroup tokens) throws ParseException {

			adjacencies = tokenIO.doHashOnFirstToken(tokens, adjacencies, TokenMap.groupAndOther(TokenMap.unit_types), TokenMap.groupAndOther(TokenMap.provinces));

		}

		public IOTarget newInstance() {
			return new prov_adj();
		}

	}

	/**
	 * Gets the powers defined by this map
	 * 
	 * @return The list of powers.
	 */
	public List<StandardToken> getPowers() {
		return powerList;
	}
	
	/**
	 * Gets all the provinces on this map
	 * @return A list of provinces on the map
	 */
	public List<Token> getProvinces() {
		if(provinceList == null) {
			provinceList = new ArrayList<Token>();
			for(Token tok : provAdj.keySet()) {
				provinceList.add(tok);
			}
		}
		
		return provinceList;
	}
}
